﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Diagnostics;
using LogLib;
using System.IO;
using System.Reflection;
using System.Collections.Concurrent;
using System.Collections;

namespace LogLib
{
    /// <summary>
    /// 专用于写日记的线程
    /// </summary>
    public class AsyncWriter : IDisposable
    {
        protected static HybridDictionary userStateToLifetime = new HybridDictionary();

        public static object LockState = new object();

        /// <summary>
        /// 存储提示信息
        /// </summary>
        private ConcurrentQueue<object> _qLogs = new ConcurrentQueue<object>();

        /// <summary>
        /// 存储提示信息列表
        /// </summary>
        //private ConcurrentQueue<LogInfoList> _qLogArray = new ConcurrentQueue<LogInfoList>();

        protected delegate void WorkerEventHandler(AsyncOperation asyncOp);

        protected SendOrPostCallback onUpdateUIDelegate;

        /// <summary>
        /// 工作线程
        /// </summary>
        protected Thread _thread = null;

        private LogConfig _config = null;

        /// <summary>
        /// 任务运行状态
        /// </summary>
        protected AsyncTaskState _state = AsyncTaskState.Uninitialized;
        public AsyncTaskState State
        {
            get
            {
                return _state;
            }
        }
 
        /// <summary>
        /// 等待处理事件
        /// </summary>
        protected ManualResetEventSlim _mresWaitHandler = new ManualResetEventSlim(false);

        public event LogErrorReportedEventHandler LogErrorReported; //声明错误报告事件
        protected void OnLogErrorReported(LogErrorReportedEventArgs e)
        {
            if (LogErrorReported != null)
            {
                LogErrorReported(this, e);
            }
        }

        protected AsyncOperation _asyncOp = null;
        public AsyncOperation AsyncOp
        {
            get { return _asyncOp; }
        }

        protected object _taskId = null;
        public object TaskId
        {
            get { return _taskId; }
        }

        public AsyncWriter()
        {
            _config = Log.Config;
            _taskId = Guid.NewGuid();
            _asyncOp = AsyncOperationManager.CreateOperation(_taskId);
            InitializeDelegates();
        }

        protected void InitializeDelegates()
        {
            onUpdateUIDelegate = new SendOrPostCallback(SendUpdateUICommand);
        }

        protected void SendUpdateUICommand(object state)
        {
            LogErrorReportedEventArgs e = state as LogErrorReportedEventArgs;
            OnLogErrorReported(e);
        }

        // This method starts an asynchronous work. 
        // First, it checks the supplied task ID for uniqueness.
        // If taskId is unique, it creates a new WorkerEventHandler 
        // and calls its BeginInvoke method to start the work.
        public void RunAsync()
        {
            if (_thread != null)
            {
                throw new Exception("The asynchronous work has started.");
            }
            // Multiple threads will access the task dictionary,
            // so it must be locked to serialize access.
            lock (userStateToLifetime.SyncRoot)
            {
                if (userStateToLifetime.Contains(_taskId))
                {
                    throw new ArgumentException(
                        "Task ID parameter must be unique",
                        "taskId");
                }
                userStateToLifetime[_taskId] = _asyncOp;
            }

            // Start the asynchronous operation.
            _thread = new Thread(Worker);
            _thread.IsBackground = true;
            _thread.Start(); 
        }

        protected bool TaskCanceled()
        {            
            return (userStateToLifetime[_taskId] == null);
        }

        public virtual void CancelAsync()
        {
            SetEvent();
            AsyncOperation asyncOp = userStateToLifetime[_taskId] as AsyncOperation;
            if (asyncOp != null)
            {
                lock (userStateToLifetime.SyncRoot)
                {
                    userStateToLifetime.Remove(_taskId);
                }
            }
        }

        /// <summary>
        /// 运行在一个线程中的循环工作
        /// </summary>
        /// <param name="asyncOp"></param>
        protected void Worker()
        {           
            try
            {
                while (!TaskCanceled())
                {
                    _state = AsyncTaskState.Waiting;
                    _mresWaitHandler.Wait();
                    if (TaskCanceled())
                    {
                        break;
                    }
                    _state = AsyncTaskState.Running;
                    OnWorkItem();
                }
            }
            catch (System.Exception ex)
            {
                LogInfo info = new LogInfo("日记处理任务", ex.Message);
                LogErrorReportedEventArgs e = new LogErrorReportedEventArgs(info, ex.ToString());
                _asyncOp.Post(onUpdateUIDelegate, e);
            }
            finally
            {
                Dispose();
                _state = AsyncTaskState.Terminated;
            }
        }

        /// <summary>
        /// 循环内工作项
        /// </summary>
        /// <param name="asyncOp"></param>
        protected void OnWorkItem()
        {
            try
            {
                _state = AsyncTaskState.Running;
                _config.CheckLogFolder();
                while (!(_qLogs.IsEmpty)
                    && !TaskCanceled()
                    && Signalled())
                {
                    object item = null;                    
                    if (_qLogs.TryDequeue(out item))
                    {
                        if(item == null)
                        {
                            continue;
                        }
                        if (item is LogInfo)
                        {

                            LogInfo logInfo = (LogInfo)item;
                            WriteLine(logInfo);
                            item = null;
                        }
                        else if (item is LogInfoList)
                        {
                            LogInfoList logList = (LogInfoList)item;
                            WriteLines(logList);
                            item = null;
                        }
                    }                  
                }            
                if (_qLogs.IsEmpty)
                {
                    Wait();
                }
            }
            catch (System.Exception ex)
            {
                //if (item != null && !item.IsEmpty())
                //{
                //    LogErrorReportedEventArgs e = new LogErrorReportedEventArgs(item, ex.ToString());
                //    _asyncOp.Post(onUpdateUIDelegate, e);
                //    return;
                //}
                //if (list != null && !list.IsEmpty())
                //{
                //    LogInfo info = new LogInfo(list.LogFullName, list.GetStrings());
                //    LogErrorReportedEventArgs e = new LogErrorReportedEventArgs(info, ex.ToString());
                //    _asyncOp.Post(onUpdateUIDelegate, e);
                //    return;
                //}
            }
        }

        /// <summary>
        /// 使等待事件获得信号, 恢复被暂停/阻塞的工作项任务
        /// </summary>
        public void SetEvent()
        {
            _mresWaitHandler.Set();
        }

        /// <summary>
        /// 判断等待对象是否获得信号,
        /// </summary>
        protected bool Signalled()
        {
            if (_mresWaitHandler != null)
            {
                return _mresWaitHandler.IsSet;
            }
            return false;
        }

        /// <summary>
        /// 暂停/阻塞任务
        /// </summary>rtual 
        public virtual void Wait()
        {
            _mresWaitHandler.Reset();
        }

        /// <summary>
        /// 等待直到后台任务终止
        /// </summary>
        public void WaitForTerminated(int time_out = 10000)
        {
            SpinWait.SpinUntil(() => (State == AsyncTaskState.Terminated), time_out);
        }

        public void Dispose()
        {
        }

        public void Stop(int time_out)
        {
            CancelAsync();
            Task.Factory.StartNew(()=>WaitForTerminated(time_out)); 
        }

        /// <summary>
        /// 将一行信息写入日记文件
        /// </summary>
        /// <param name="fullFilePath">日记文件路径</param>
        /// <param name="line">一行日记信息</param>
        public void WriteLine(LogInfo info)
        {
            using (FileStream fs = new FileStream(info.LogFullName, FileMode.OpenOrCreate, FileAccess.Write))
            {
                using (StreamWriter m_streamWriter = new StreamWriter(fs))
                {
                    m_streamWriter.BaseStream.Seek(0, SeekOrigin.End);
                    m_streamWriter.WriteLine(info.Content);
                    m_streamWriter.Flush();
                    m_streamWriter.Close();                  
                }
                fs.Close();
            }
        }

        /// <summary>
        /// 将多行信息写入日记文件
        /// </summary>
        /// <param name="fullFilePath">日记文件路径</param>
        /// <param name="strContentLine">一行日记信息</param>
        public void WriteLines(LogInfoList infos)
        {
            using (FileStream fs = new FileStream(infos.LogFullName, FileMode.OpenOrCreate, FileAccess.Write))
            {
                using (StreamWriter m_streamWriter = new StreamWriter(fs))
                {
                    m_streamWriter.BaseStream.Seek(0, SeekOrigin.End);
                    foreach (string line in infos.ListLines)
                    {
                        if (!string.IsNullOrEmpty(line))
                        {
                            m_streamWriter.WriteLine(line);
                        }
                    }
                    m_streamWriter.Flush();
                    m_streamWriter.Close();
                }
                fs.Close();
            }           
        }

        public void AddLogInfo(LogInfo info)
        {
            _qLogs.Enqueue(info);
            SetEvent();
        }

        public void AddLogInfoList(LogInfoList info)
        {
            _qLogs.Enqueue(info);
            SetEvent();
        }
    }

}
